package com.hrpc.channelHandler.handler;

import com.hrpc.compress.Compressor;
import com.hrpc.compress.CompressorFactory;
import com.hrpc.serialize.Serialize;
import com.hrpc.serialize.SerializeFactory;
import com.hrpc.transport.message.MessageFormatConstant;
import com.hrpc.transport.message.RequestPayload;
import com.hrpc.transport.message.hRpcResponse;
import com.hrpc.transport.message.hRpcResponse;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

/**
 * 报文的内容：
 * 4B（magic）--->hrpc.getByte()
 * 1B(version)--->1
 * 2B(header length)--->首部的长度
 * 4B(full length)--->报文总长度 解决粘包问题：定长/自己设置长度进行获取
 * 1B(code) 状态码
 * 1B(serialize) 序列化
 * 1B(compress) 压缩
 * 8B(requestId) 请求ID
 * body 长度不定，可以使用full_length - header_length 来计算
 * 出站时第一个经过的处理器
 */
public class hRpcResponseEncoder extends MessageToByteEncoder<hRpcResponse> {

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, hRpcResponse hRpcResponse, ByteBuf byteBuf) throws Exception {
        //封装byteBuf
        byteBuf.writeBytes(MessageFormatConstant.MAGIC);
        //一个字节的版本号
        byteBuf.writeByte(MessageFormatConstant.VERSION);
        //2个字节的头部长度
        byteBuf.writeShort(MessageFormatConstant.HEADER_LENGTH);
        //writeIndex向右移动几位，但是不写
        byteBuf.writerIndex(byteBuf.writerIndex()+MessageFormatConstant.FULL_FIELD_LENGTH);
        //3个类型
        byteBuf.writeByte(hRpcResponse.getCode());
        byteBuf.writeByte(hRpcResponse.getSerializeType());
        byteBuf.writeByte(hRpcResponse.getCompressType());
        //8字节的请求ID
        byteBuf.writeLong(hRpcResponse.getRequestId());
        //时间戳
        byteBuf.writeLong(hRpcResponse.getTimeStamp());

//        //如果是心跳请求，独立处理一下总长度
        //写入请求体（RequestPayLoad）
//        byte[] bodyBytes = getBodyBytes(hRpcResponse.getBody());
        //1.需要对响应进行序列化
        Serialize serialize= SerializeFactory.getSerialize(hRpcResponse.getSerializeType()).getImpl();
        byte[] bodyBytes =null;
        //2.要做压缩
        if (hRpcResponse.getBody()!=null){
            bodyBytes = serialize.serialize(hRpcResponse.getBody());
            Compressor compressor = CompressorFactory.getCompressor(hRpcResponse.getCompressType()).getImpl();
            bodyBytes = compressor.compress(bodyBytes);
        }

        if (bodyBytes != null){
            byteBuf.writeBytes(bodyBytes);
        }
        int bodyLength = bodyBytes==null ? 0 : bodyBytes.length;
        //重新处理报文的总长度
        //先保存当前写指针的位置
        int index = byteBuf.writerIndex();
        //将写指针的位置移动到总长度的位置上
        byteBuf.writerIndex(MessageFormatConstant.MAGIC.length +
                MessageFormatConstant.VERSION_LENGTH +
                MessageFormatConstant.HEADER_LENGTH_LENGTH);
        byteBuf.writeInt(MessageFormatConstant.HEADER_LENGTH+ bodyLength);
        //将写指针归位
        byteBuf.writerIndex(index);
    }

    private byte[] getBodyBytes(Object body) {
        //针对不同的消息类型需要做不同的处理，心跳请求，没有payload
        if (body == null){
            return null;
        }
        //希望可以通过一些设计模式，面向对象的编程，让我们可以配置修改序列化和压缩的方式
        //对象怎么变成一个字节数组：序列化、压缩
        try {
            ByteArrayOutputStream baos=new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
            objectOutputStream.writeObject(body);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("序列化时出现异常");
        }

    }
}
